﻿using Org.BouncyCastle.Asn1.GM;
using Org.BouncyCastle.Asn1.X9;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Utilities.Encoders;
using System.Text;
using UnityEngine;

/// <summary>
/// SM2 加密解密示例
/// </summary>
public class SM2Sample : MonoBehaviour
{
    private byte[] pubKey;
    private byte[] privKey;

    private string content = "加密的内容";

    private void Start()
    {
        GenerateKeyPair(out string priKeyStr, out string pubKeyStr);
        privKey = Hex.Decode(priKeyStr);
        pubKey = Hex.Decode(pubKeyStr);

        var str = EncryptString(content);
        Debug.Log($"加密后 { str } ");
        Debug.Log($"解密后 { DecryptString(str) }");
    }

    /// <summary>
    /// 生成密钥对
    /// </summary>
    /// <param name="privateKeyHex"></param>
    /// <param name="publicKeyHex"></param>
    public static void GenerateKeyPair(out string privateKeyHex, out string publicKeyHex)
    {
        // 获取 SM2 椭圆曲线参数
        X9ECParameters sm2Params = ECNamedCurveTable.GetByName("sm2p256v1");
        ECDomainParameters domainParams = new ECDomainParameters(
            sm2Params.Curve,
            sm2Params.G,
            sm2Params.N,
            sm2Params.H
        );

        // 初始化密钥对生成器
        ECKeyPairGenerator generator = new ECKeyPairGenerator();
        ECKeyGenerationParameters keyGenParams = new ECKeyGenerationParameters(
            domainParams,
            new SecureRandom() // 使用安全的随机数生成器
        );
        generator.Init(keyGenParams);

        // 生成密钥对
        AsymmetricCipherKeyPair keyPair = generator.GenerateKeyPair();

        // 提取私钥 (BigInteger D)
        ECPrivateKeyParameters privateKey = (ECPrivateKeyParameters)keyPair.Private;
        byte[] privateKeyBytes = privateKey.D.ToByteArrayUnsigned(); // 转换为无符号字节数组
        privateKeyHex = Hex.ToHexString(privateKeyBytes);

        // 提取公钥 (ECPoint Q)
        ECPublicKeyParameters publicKey = (ECPublicKeyParameters)keyPair.Public;
        byte[] publicKeyBytes = publicKey.Q.GetEncoded(false); // 非压缩格式（含04前缀）
        publicKeyHex = Hex.ToHexString(publicKeyBytes);
    }


    /// <summary>
    /// 解密
    /// </summary>
    /// <param name="data"></param>
    /// <param name="privateKey"></param>
    /// <returns></returns>
    public byte[] Decrypt(byte[] data, byte[] privateKey)
    {
        BigInteger d = new BigInteger(1, privateKey);
        X9ECParameters spar = ECNamedCurveTable.GetByName("sm2p256v1");
        ECDomainParameters ecdp = new ECDomainParameters(spar.Curve, spar.G, spar.N, spar.H);

        // 创建私钥参数
        ECPrivateKeyParameters keyParameter = new ECPrivateKeyParameters(d, ecdp);

        SM2Engine engine = new SM2Engine();
        engine.Init(false, keyParameter);

        byte[] decryptedData = engine.ProcessBlock(data, 0, data.Length);
        return decryptedData;
    }

    /// <summary>
    /// 解密
    /// </summary>
    /// <param name="data"></param>
    /// <returns></returns>
    public string DecryptString(string data)
    {
        byte[] encryptedData = Hex.Decode(data);
        byte[] decryptedData = Decrypt(encryptedData, privKey);
        return Encoding.UTF8.GetString(decryptedData);
    }


    /// <summary>
    /// 加密
    /// </summary>
    /// <param name="data"></param>
    /// <param name="publicKey"></param>
    /// <returns></returns>
    public byte[] Encrypt(byte[] data, byte[] publicKey)
    {
        X9ECParameters sm2Params = GMNamedCurves.GetByName("sm2p256v1");
        Org.BouncyCastle.Math.EC.ECPoint q = sm2Params.Curve.DecodePoint(publicKey);
        ECDomainParameters domainParameters = new ECDomainParameters(sm2Params.Curve, sm2Params.G, sm2Params.N, sm2Params.H);
        AsymmetricKeyParameter keyParameter = new ECPublicKeyParameters(q, domainParameters);
        SM2Engine engine = new SM2Engine();
        engine.Init(true, new ParametersWithRandom(keyParameter, new SecureRandom()));
        byte[] encryptedData = engine.ProcessBlock(data, 0, data.Length);
        return encryptedData;
    }


    /// <summary>
    /// 加密
    /// </summary>
    /// <param name="data"></param>
    /// <returns></returns>
    public string EncryptString(string data)
    {
        byte[] dataBytes = Encoding.UTF8.GetBytes(data);
        byte[] encryptedData = Encrypt(dataBytes, pubKey);
        return Hex.ToHexString(encryptedData);
    }
}